package myspring;

import myspring.annotation.Autowired;
import myspring.annotation.Component;
import myspring.annotation.Qualifier;
import myspring.annotation.Value;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Map;

public class MyAnnotationConfigApplicationContext {

    private Map<String,Object> ioc = new HashMap<>();

    public MyAnnotationConfigApplicationContext(String packageName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

        //获取包下所有的类
        Set<BeanDefinition> beanDefinitions = findBeanDifinition(packageName);

        //根据原材料创建bean
        createObject(beanDefinitions);

        //自动装载,可根据名字和类型转载
        autowiredObject(beanDefinitions);
    }

    /**
     * 该方法是给对象中的引用属性赋值
     * 即加了@Autowired注解的属性
     * @param beanDefinitionSet
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public void autowiredObject(Set<BeanDefinition> beanDefinitionSet) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Iterator<BeanDefinition> iterator = beanDefinitionSet.iterator();
        while (iterator.hasNext()){
            BeanDefinition beanDefinition = iterator.next();
            Class clazz = beanDefinition.getBeanClass();
            Field[] fields = clazz.getDeclaredFields();
            for(Field field : fields){
                Autowired autowired = field.getAnnotation(Autowired.class);
                if (autowired != null){
                    Qualifier qualifier = field.getAnnotation(Qualifier.class);
                    if (qualifier != null){

                        //ByName
                        String value = qualifier.value();
                        Object bean= getBean(value);
                        String fieldName = field.getName();
                        String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
                        Method method = clazz.getMethod(methodName, field.getType());
                        Object object = getBean(beanDefinition.getBeanName());
                        method.invoke(object, bean);
                    }else{

                        //ByType
                        String fieldName = field.getName();
                        Object bean= getBean(fieldName);
                        String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
                        Method method = clazz.getMethod(methodName, field.getType());
                        Object object = getBean(beanDefinition.getBeanName());
                        method.invoke(object, bean);

                    }
                }
            }
        }

    }

    /**
     * 从容器中得到bean
     * @param beanName
     * @return
     */
    public Object getBean(String beanName){
        return ioc.get(beanName);
    }

    /**
     * 初始化对象，并且通过set方法来给对象的属性赋初始值
     * @param beanDefinitionSet
     */
    public void createObject(Set<BeanDefinition> beanDefinitionSet){
        Iterator<BeanDefinition> iterator = beanDefinitionSet.iterator();
        while (iterator.hasNext()){
            BeanDefinition beanDefinition = iterator.next();
            Class clazz = beanDefinition.getBeanClass();
            String beanName = beanDefinition.getBeanName();
            try {
                Object object = clazz.getConstructor().newInstance();
                //完成属性的赋值
                Field[] declaredFields = clazz.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    Value valueAnnotation = declaredField.getAnnotation(Value.class);
                    if(valueAnnotation!=null){
                        String value = valueAnnotation.value();
                        String fieldName = declaredField.getName();
                        String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
                        Method method = clazz.getMethod(methodName,declaredField.getType());
                        //完成数据类型转换
                        Object val = null;
                        switch (declaredField.getType().getName()){
                            case "java.lang.Integer":
                                val = Integer.parseInt(value);
                                break;
                            case "java.lang.String":
                                val = value;
                                break;
                            case "java.lang.Float":
                                val = Float.parseFloat(value);
                                break;
                        }
                        method.invoke(object, val);
                    }
                }
                ioc.put(beanName,object);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 找到有Component注解的类，表示要交给容器来管理
     * @param packageName
     * @return
     */
    public Set<BeanDefinition> findBeanDifinition(String packageName){
        //获取包下的所有类
        //遍历类
        //将类封装成beanDefinition，转载到集合
        Set<Class<?>> classes = MyTools.getClasses(packageName);
        Set<BeanDefinition> beanDefinitionSet = new HashSet<>();
        Iterator<Class<?>> iterator = classes.iterator();
        while (iterator.hasNext()){
            Class<?> clazz = iterator.next();
            Component component = clazz.getAnnotation(Component.class);

            if(component != null){
                //获取Component注解的值
                String beanName = component.value();
                if("".equals(beanName)){
                    //获取类名首字母小写
                    String className = clazz.getName().replaceAll(clazz.getPackage().getName() + ".", "");
                    beanName = className.substring(0, 1).toLowerCase()+className.substring(1);
                }
                BeanDefinition beanDefinition = new BeanDefinition(beanName,clazz);
                beanDefinitionSet.add(beanDefinition);
                //System.out.println(beanName);
            }
            //System.out.println(clazz+"--"+component);
        }
        return beanDefinitionSet;
    }
}
